/*
 * User: rant
 * Date: 24/12/2006
 *
 *
 */
package laMalinche.rete;

import laMalinche.Pair;
import laMalinche.DevMode;
import laMalinche.rete.nodes.*;
import laMalinche.rete.rule.*;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.util.*;

import com.google.inject.Inject;

/**
 *
 */
public abstract class ReteRoot implements Rete {
    @Inject
    private  ConditionNodeFactory conditionNodeFactory;
    private static Logger log = LogManager.getLogger(ReteRoot.class);
 	private final Set<MergerNode> mergerSet = new HashSet<MergerNode>();

	public RuleMap getRuleMap() {
		return ruleMap;
	}

	public void setRuleMap(final RuleMap ruleMap) {
		this.ruleMap = ruleMap;
	}

	private RuleMap ruleMap;

	protected ReteRoot() {
        DevMode.getInstance().getInjector().injectMembers(this);
		ruleMap = new BasicRuleMap();
	}

	public ConditionNodeFactory getConditionNodeFactory() {
		return conditionNodeFactory;
	}

	public Map<Pair<?, BusinessRuleType>, ConditionNode> getCondiMap() {
		return conditionNodeFactory.getCondiMap();
	}

	protected Collection<ConditionNode> getConditions() {
		final Collection<ConditionNode> nodes = getCondiMap().values();
		final List<ConditionNode> list = new ArrayList<ConditionNode>(nodes);
		Collections.sort(list);
//		Collections.reverse(list);
		return list;
	}

	public void initializeConditions() {
		for (ConditionNode condition : getConditions()) {
			condition.initialize();
		}
	}

	public void updateConditions() {
		for (ConditionNode condition : getConditions()) {
			condition.update();
		}
	}

	public void add(@NotNull ProductionRule rule) {

		final Stack<Relevant> reteNodesStack = conditionNodeFactory.getConditions(rule);
		while (reteNodesStack.size() > 1) {
			MergerNode mergerNode = noExceptionsNewInstance(getMergerNodeType());
			mergerNode.addLeftNode(reteNodesStack.pop());
			mergerNode.addRightNode(reteNodesStack.pop());
			if (!mergerSet.contains(mergerNode)) {
				mergerSet.add(mergerNode);
			}
			reteNodesStack.add(0, mergerNode);
		}
		final ReteNode lastMergerNode = reteNodesStack.pop();
		final ActivatorNode activatorNode = new ActivatorNode();
		activatorNode.addMerger(lastMergerNode);
		Action action = rule.getAction();
//		final Action action1 = conditionNodeFactory.getObservableAction(rule);
		activatorNode.setRuleActivator(action);
//		rule.setAction(action1);
		// todo this is the original rule not the one with the new conditions
		ruleMap.registerRule(rule);
	}

	protected <T> T noExceptionsNewInstance(Class<T> clazz) {
		T result;
		try {
			result = clazz.newInstance();
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		return result;
	}

	public void detach() {
		for (ConditionNode conditionNode : getConditions()) {
			conditionNode.detach();
		}
		ruleMap = null;
	}

	private Class<? extends MergerNode> getMergerNodeType() {
		return MergerNode.class;
	}
}